package actors {
	import __AS3__.vec.Vector;
	
	import common.FMath;
	
	import flash.display.Sprite;
	import flash.geom.Point;
	
	public class Fish extends Actor {
		private var _color:uint;
		
		private var _crudPerception:int;
		private var _fishPerception:int;
		
		private var _crudInRange:Vector.<Actor>;
		private var _fishInRange:Vector.<Actor>;
		
		private var _crudInRangeUpdated:Boolean;
		private var _fishInRangeUpdated:Boolean;
		
		public function Fish(locIn:Point) {
			super();
			
			_availableCommands = new Vector.<String>();
			_availableCommands.push("goForward",
				"turnUp",
				"turnDown",
				"eat",
				"mate",
				"idle");
			
			_crudInRange = new Vector.<Actor>();
			_fishInRange = new Vector.<Actor>();
			
			//fish constants
			_friction = .95;
			_speed = 0;
			_rotation = Math.random() * 2 * Math.PI;
			_x = locIn.x;
			_y = locIn.y;
			
			_graphic = new Sprite();
		}
		
		override public function init():void {
			_size = _sizeMax / 8;
			_ageMax = _sizeMax * 2500;
			_energy = energyMax;

			generateGraphic();
		}
		
		override public function update():Boolean {
			_crudInRangeUpdated = false;
			_fishInRangeUpdated = false;
			
			_energy --;
			
			return super.update();
		}
		
		override protected function generateGraphic():void {
			var sprite:Sprite = Sprite(_graphic);
			sprite.graphics.clear();
			sprite.graphics.lineStyle(1, _color);
			FMath.drawArc(sprite.graphics, -_size * .7, 0, _size, 45/360, -115/360, _size + 5);
			FMath.drawArc(sprite.graphics, -_size * .7, 0, _size, 45/360, -115/360, _size + 5, true);
		}

		//so we only have to sort and dist compare when needed
		private function updateCrudInRange():void {
			if (_crudInRangeUpdated)
				return;
			
			_crudInRange = _actorMaster.findInRange(loc, _crudPerception, null, false, Crud);			
			_crudInRangeUpdated = true;
		}

		private function updateFishInRange():void {
			if (_fishInRangeUpdated)
				return;
			
			_fishInRange = _actorMaster.findInRange(loc, _fishPerception, null, false, Fish);
			_fishInRangeUpdated = true;
		}
		
		override public function eat():void {
			updateCrudInRange();
			updateFishInRange();
			
			var inRange:Vector.<Actor> = new Vector.<Actor>();
			//TODO: should these should be sorted?
			inRange = inRange.concat(_crudInRange);
			inRange = inRange.concat(_fishInRange);
			if (inRange.length <= 1)
				return;
			
			//find which one we can eat
			for (var i:int = 0; i < inRange.length; i ++) {
				var eatTarget:Actor = inRange[i];
				
				if (Point.distance(loc, eatTarget.loc) > eatRange)
					continue;
				
				//multiply by 1.25 because fish can't eat fish about the same size
				if (eatTarget.size * 1.25 < size || eatTarget is Crud) {
					energy += eatTarget.energy;
					eatTarget.energy = -1;
					break;
				}
			}
		}
		
		//DEBUG:
//		public var mateCount:int = 0;
		override public function mate():void {
			//if fish isn't mature
			if (!mature)
				return;
			
			updateFishInRange();
			
			if (_fishInRange.length <= 1)
				return;
				
			//find which one we can mate with
			for (var i:int = 0; i < _fishInRange.length; i ++) {
				var mateTarget:Fish = Fish(_fishInRange[i]);
				
				//don't mate with self
				if (mateTarget === this)
					continue;
					
				if (!mateTarget.mature)
					continue;
				
				if (Point.distance(loc, mateTarget.loc) > mateRange)
					continue;
					
				//DEBUG:
//				mateCount ++;
//				mateTarget.mateCount ++;
//				if (mateCount > 5 || mateTarget.mateCount > 5) {
//					trace("mateCount: " + mateCount);
//					trace("mateTarget.mateCount: " + mateTarget.mateCount);
//				}
				
				//make some babies!
				while (true) {

					//create and init baby properties
					var baby:Fish = new Fish(loc);
					baby.color = (_color + mateTarget.color) / 2;
					baby.crudPerception = FMath.pickRand(_crudPerception, mateTarget.crudPerception);
					baby.fishPerception = FMath.pickRand(_fishPerception, mateTarget.fishPerception);
					baby.sizeMax = (_sizeMax + mateTarget.sizeMax) / 2;
					baby.accel = FMath.pickRand(_accel, mateTarget.accel);
					baby.init();
					
					//set new program
					baby.program = FMath.pickRand(_program.clone(), mateTarget.program.clone());
					baby.program.mutate();
					baby.program.actor = baby;
					trace(baby.program);
//					baby.program = program.blend(mateTarget.program);
//					baby.program.actor = baby;
					
					var contrib:int = baby.energy / 2;
					var mateContrib:int = baby.energy / 2;
					var tooWeak:Boolean = false;
					var mateTooWeak:Boolean = false;
					if (energy < contrib) {
						mateContrib += contrib - energy;
						tooWeak = true;
					}
					if (mateTarget.energy < mateContrib) {
						contrib += mateContrib - mateTarget.energy;
						mateTooWeak = true;
					}
					
					if (tooWeak && mateTooWeak)
						break;
					
					energy -= contrib;
					mateTarget.energy -= mateContrib;

					_actorMaster.addActor(baby);
				}
				
				break;
			}
		}
		
		public function idle():void {
			//DO NOTHING
		}
	
		private function randCommand(actor:Actor):String {
			var commands:Vector.<String> = actor.availableCommands;
			var index:int = Math.random() * commands.length;
			return commands[index];
		}
		
		//getters/setters
		public function get color():uint {
			return _color;
		}
		
		public function set color(colorIn:uint):void {
			_color = colorIn;
		}
		
		public function get crudPerception():int {
			return _crudPerception
		}
		
		public function set crudPerception(crudPerceptionIn:int):void {
			_crudPerception = crudPerceptionIn;
		}
		
		public function get fishPerception():int {
			return _fishPerception
		}
		
		public function set fishPerception(fishPerceptionIn:int):void {
			_fishPerception = fishPerceptionIn;
		}
		
		override public function get memory():Array {
			updateCrudInRange();
			updateFishInRange();
			
			var memArray:Array = super.memory;
			memArray.push(color,
				crudPerception,
				fishPerception,
				_crudInRange,
				_fishInRange);
			
			return memArray;
		}
		
		override public function get observableProps():Array {
			var observeArray:Array = super.observableProps;
			observeArray.push(color);
			
			return observeArray;
		}
		
		override public function destroy():void {
			var actor:Actor;
			for each (actor in _crudInRange) {
				actor.destroy();
			}
			_crudInRange = null;
			
			for each (actor in _fishInRange) {
				actor.destroy();
			}
		}
	}
}
